package educar.db;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import com.mysql.jdbc.PreparedStatement;
import com.mysql.jdbc.Statement;

public class JPA {
	public static ResultSet runQuery(String sentence){
        ResultSet res = null;
        try {
            Statement sent = (Statement) DbConnection.getInstance().createStatement();
            res = sent.executeQuery(sentence);
        }
        catch (Exception ex){
            ex.printStackTrace();
        } 
        return res;
    }

	
	public static void runUpdate(String sentence) throws SQLException{
            Statement stm = (Statement) DbConnection.getInstance().createStatement();
            stm.executeUpdate(sentence);
	}
	    
    /**
     * Create the statement to insert into the table
     * @param table "name of table"
     * @param columns "name of columns to use in the insertion"
     * @return PreparedStatement "Statement to insert, to modify values to insert set this object"
     *
     * i.e.:
     * DB: db
     * Table: table
     * Columns: id (integer) y name (text)
     *
     *   String[] columns = {"id", "name"};
     *   PreparedStatement stm = conn.newRecord("db.table", columns);
     *   // Set id with 3
     *   stm.setInt(1, 3);
     *   
     *   // Set name
     *   stm.setString(2, "Text to insert");
     *   
     *   // do insertion
     *   conn.create(stm);
     */
    public PreparedStatement newRecord(String table, String[] columns){
        PreparedStatement stmt = null;
        try {
            String values = "";
            String columnNames = "";
            int i = 0;
            while (i != columns.length){
                values += "?,";
                columnNames += (columnNames.equals(""))? columns[i] : ", " + columns[i];
                i++;
            }
            values = (String) values.subSequence(0, values.length()-1);

            String command = "INSERT INTO " + table + " (" + columnNames + ") VALUES (" + values +")";
            stmt = (PreparedStatement) DbConnection.getInstance().prepareStatement(command);
        }
        catch (Exception ex){
            System.err.println("ERROR createInsertStatement");
            ex.printStackTrace();
        }
        return stmt;
    }

    /**
     * do insertion
     * @param stm to insert
     * 
     */
    public void create(java.sql.PreparedStatement stm) {
        try {
            stm.executeUpdate();
        }
        catch  (Exception ex){
            ex.printStackTrace();
        }
    }
    
    /**
     * Edit content
     * @param sentence SQL 
     * (i.e. "UPDATE table set name = 'TEST NAME' where id = 10")
     *
     * i.e.:
     *
     * conexion.update("UPDATE db.table set name= 'TEST NAME' where id = 10");
     * @throws SQLException 
     */
    public void update(String tableName, String column, String value,String id) throws SQLException{
    	String query = "UPDATE " + DbConnection.bd + "." + tableName; 
		query += " set " + column + "= \"" + value + "\" WHERE id=" + id + ";";

		runUpdate(query);
    }
    
    /**
     * Edit content
     * @param sentence SQL
     * @param by permite elejir por cual columna se edita (columna de la clave) 
     * (i.e. "UPDATE table set name = 'TEST NAME' where id = 10")
     *
     * i.e.:
     *
3     * conexion.update("UPDATE db.table set name= 'TEST NAME' where id = 10");
     */
    public void update(String tableName, String column, String value, String by, String id) throws SQLException{
    	String query = "UPDATE " + DbConnection.bd + "." + tableName; 
		query += " set " + column + "= \"" + value + "\" WHERE "+by+"=" + id + ";";

		runUpdate(query);
    }
    
    
    public void update(String tableName, String column, String value, String[] by, String[] key) throws SQLException{
    	String condition = new String();
    	for(int i=0;i<by.length;i++) 
        	if (i < by.length-1)
        		condition += by[i] + "= \"" + key[i] + "\"" + "&&";
        	else
        		condition += by[i] + "= \"" + key[i] + "\";";
    	String query = "UPDATE " + DbConnection.bd + "." + tableName; 
		query += " set " + column + "= \"" + value + "\" WHERE " + condition;

		runUpdate(query);
    }

    /**
     * Permite actualizar una fila completa de la base de datos
     * @param tableName "nombre de la tabla"
     * @param columns "arreglo de string con las columnas a modificar"
     * @param value "arreglo de string con los valores de las columnas a modificar"
     * @param by "establece por cual columna se va a fijar"
     * @param key "establece el valor de la columna por la cual se va afijar"
     * @throws SQLException 
     */
    public void update(String tableName, String[] columns, String[] value, String by, String key) throws SQLException {
    	for (int i = 0;i<columns.length;i++) {
    		update(tableName,columns[i],value[i],by,key);
    	}
    }
    
    
    
    /**
     * Permite actualizar una fila completa de la base de datos bajo varias condiciones
     * @param tableName "nombre de la tabla"
     * @param columns "arreglo de string con las columnas a modificar"
     * @param value "arreglo de string con los valores de las columnas a modificar"
     * @param by "arreglo de String que establece por cuales columna se va a comparar"
     * @param key "arreglo de String que establece los valores de las columnas por las cuales se van a comparar"
     * @throws SQLException 
     */
    public void update(String tableName, String[] columns, String[] value, String[] by, String[] key) throws SQLException {
    	for (int i = 0;i<columns.length;i++) {
    		update(tableName,columns[i],value[i],by,key);
    	}
    }
    
    
    /**
     * destruye una tabla en la base de datos
     * @param tableName "nombre de la tabla"
     * @param columnName "nombre de la columna con la que quiero comparar mi identificador"
     * @param id "el identificador de la fila que estoy buscando para eliminar"
     * @throws SQLException 
     */
    public void destroy(String tableName, String columnName, String id) throws SQLException{
    	String query = "DELETE from " + DbConnection.bd + "." + tableName; 
    	query += " Where " + columnName + " = \"" + id +"\";";
    	
		runUpdate(query);
    }
    
    
    /**
     * destruye una tabla en la base de datos
     * @param tableName "nombre de la tabla"
     * @param columnName "Arreglo de String con los nombres de las columnas con la que quiero comparar mis identificadores"
     * @param id "Arreglo de Sring con los identificadores de las filas que estoy buscando para eliminar"
     * @throws SQLException 
     */
    public void destroy(String tableName, String[] columnName, String[] id) throws SQLException{
    	String condition = new String();
    	for(int i=0;i<columnName.length;i++) 
        	if (i < columnName.length-1)
        		condition += columnName[i] + "= \"" + id[i] + "\"" + "&&";
        	else
        		condition += columnName[i] + "= \"" + id[i] + "\";";
    	String query = "DELETE from " + DbConnection.bd + "." + tableName; 
    	query += " Where " + condition;
    	
		runUpdate(query);
    }
    
    /**
     * Shows all records from a table
     * @param tableName
     *
     * i.e.:
     *   conn.showAll("table");
     */
    public ResultSet showAll(String tableName){
     	ResultSet result = null;
    	String query = "SELECT * from " + DbConnection.bd + "." + tableName + ";";

    	result = runQuery( query );
        
    	return result;
    }
    
    
    /**
     * Shows one record
     * @param tableName
     * @param id
     *
     * i.e.:
     *   conn.show("table", 1);
     */
    public ResultSet show(String tableName, String id){
     	ResultSet result = null;
    	String query = "SELECT * from " + DbConnection.bd + "." + tableName + " WHERE id=" + id + ";";

    	result = runQuery( query );
        
    	return result;
    }
    
    /**
     * Muestra la proyeccion de una tabla
     * @param tableName String "nombre de la cabla"
     * @param colunms  String "lista de columnas ej. "dni,nombre" "
     * @return ResultSet "contiene diche proyeccion"
     */
    public ResultSet showProyection(String tableName, String colunms){
     	ResultSet result = null;
    	String query = "SELECT "+colunms+" from " + DbConnection.bd + "." + tableName+ ";";

    	result = runQuery( query );
        
    	return result;
    }
    
    
    /**
     * Muestra la proyeccion de una tabla por un campo dado (tipo select by)
     * @param tableName String "nombre de la cabla"
     * @param colunms  String "lista de columnas ej. "dni,nombre" "
     * @return ResultSet "contiene diche proyeccion"
     */
    public ResultSet showProyection(String tableName, String colunms,String fieldName, String value){
    	ResultSet result = null;
    	String query = "SELECT "+colunms+" from " + DbConnection.bd + "." + tableName + " WHERE " + fieldName + "= \"" + value + "\";";
    	
    	result = runQuery( query );
    	
    	return result;
    }
    
    
    
    /**
     * Muestra la proyeccion de una tabla por un campo dado (tipo select by)
     * luego a partir de un campo permite establecer una condicion y tambien
     * fijarse que no este en otra tabla
     * se pueden hacer cosas como
     * 
     * SELECT username,password FROM users WHERE role='student' AND username NOT IN (SELECT username FROM alumno);
     * 
     * @param tableName String "nombre de la tabla"
     * @param colunms  String "lista de columnas ej. "dni,nombre" "
     * @param fieldname String "columna por la cual va a comparar"
     * @param value String "con el valor de la columna que va comparar para quedarse"
     * @param secondTable String "contra la tabla que comparar"
     * @param byColumn String "columna en comun entre ambas tablas"
     * @return ResultSet "contiene diche proyeccion"
     */
    public ResultSet showNotIn(String tableName, String colunms,String fieldName, String value,String secondTalbe,String byColunm){
    	ResultSet result = null;
    	String query = "SELECT " + colunms + " from " + DbConnection.bd + "." + tableName + " WHERE " + fieldName + "= \"" + value + "\" "
    			+ " AND " + byColunm + " NOT IN (SELECT "+ byColunm +" from "+ DbConnection.bd + "." + secondTalbe + " WHERE NOT "+ byColunm + " is null" + ");";
    	   	
    	result = runQuery( query );
    	
    	return result;
    }
    
    
    /**
     * Permite hacer cosas parecidas al anterior pero con menos restricciones
     * ademas no necesariamente las columnas por las que compara tienen que
     * tener el mismo nombre
     * 
     * ej: SELECT `dni` from `docente` where `dni` not in (select `responsable` from `materia`);
     */
    public ResultSet showNotIn(String tableName, String colunms,String byColunm,String secondTalbe,String column){
    	ResultSet result = null;
    	String query = "SELECT " + colunms + " from " + DbConnection.bd + "." + tableName + " WHERE " + byColunm 
    			+ " NOT IN (SELECT "+ column +" from "+ DbConnection.bd + "." + secondTalbe + " WHERE NOT "+ column + " is null" +");";
    	   	
    	result = runQuery( query );
    	
    	return result;
    }
    

    
    /**
     * Consulta a la base de datos que la condicion tiene un NOT IN
     * @param firstTable "String con el nombre de la primera tabla"
     * @param colunmsFst "String con las columnas a devolver"
     * @param byFst "String con la condicion de la primera tabla"
     * @param secondTable "String con el nombre de la segunda tabla"
     * @param columnsSnd "String con el nombre de la columna a devolver por la segunda tabla"
     * @param bySnd "Arreglo de String con las columnas a comparar en la condicion del where"
     * @param value "Arreglo de String con los valores que se deben comparar en la condicion del where"
     * @return ResultSet
     */
    public ResultSet showNotIn(String firstTable, String colunmsFst,String byFst,String secondTable,String columnSnd,String[] bySnd,String[] value){
    	ResultSet result = null;
    	String condition = new String();
    	for(int i=0;i<bySnd.length;i++) 
        	if (i < bySnd.length-1)
        		condition += bySnd[i] + "= \"" + value[i] + "\"" + "&&";
        	else
        		condition += bySnd[i] + "= \"" + value[i] + "\"";
    	
    	String query = "SELECT " + colunmsFst + " from " + DbConnection.bd + "." + firstTable + " WHERE " + byFst 
    			+ " NOT IN (SELECT "+ columnSnd +" from "+ DbConnection.bd + "." + secondTable + " WHERE "+ condition + "AND NOT "+ columnSnd + " is null);";
    	   	
    	result = runQuery( query );
    	
    	return result;
    }
    
    
    
    
    /**
     * dados los parametros devuelve una fila de la base de datos
     * @param tableName "String con el nombre de la tabla en la que voy a consultar"
     * @param fieldName "String con el nombre de la columna a comparar"
     * @param value "String con el valor que qiero buscar en la columna"
     * @return ResultSet "que contiene una tupla de la base de datos"
     */
    public ResultSet getByField(String tableName, String fieldName, String value){
    	ResultSet result = null;
    	String query = "SELECT * from " + DbConnection.bd + "." + tableName + " WHERE " + fieldName + "= \"" + value + "\";";
    	
    	result = runQuery( query );
    	
    	return result;
    }
    
    
    
    /**
     * dados los parametros devuelve una fila de la base de datos
     * @param tableName "String con el nombre de la tabla en la que voy a consultar"
     * @param columnas "string con las columnas a mostrar estilo sql (columna1,columna2,...,columnaN)"
     * @param fieldName "String con el nombre de la columna a comparar"
     * @param value "String con el valor que qiero buscar en la columna"
     * @return ResultSet "que contiene una tupla de la base de datos"
     */
    public ResultSet getByField(String tableName,String columnas, String fieldName ,String value){
    	ResultSet result = null;
    	String query = "SELECT "+ columnas +" from " + DbConnection.bd + "." + tableName + " WHERE " + fieldName + "= \"" + value + "\";";
    	
    	result = runQuery( query );
    	
    	return result;
    }
    
    
    /**
     * dados los parametros devuelve una fila de la base de datos
     * @param tableName "String con el nombre de la tabla en la que voy a consultar"
     * @param fieldName "Arreglo de String con los nombre de las columnas a comparar"
     * @param value "Arreglo de String 	con los valores que qiero buscar en las columnas"
     * @return ResultSet "que contiene una tupla de la base de datos"
     */
    public ResultSet getByField(String tableName, String[] fieldName, String[] value){
    	ResultSet result = null;
    	String condition = new String();
    	for(int i=0;i<fieldName.length;i++) 
        	if (i < fieldName.length-1)
        		condition += fieldName[i] + "= \"" + value[i] + "\"" + "&&";
        	else
        		condition += fieldName[i] + "= \"" + value[i] + "\";";
    	
    	String query = "SELECT * from " + DbConnection.bd + "." + tableName + " WHERE " + condition;

    	result = runQuery( query );
    	
    	return result;
    }
    
    
    /**
     * Pasa un resultSet a una Matriz de Objetos
     * @param rs  "resultSet a convertir"
     * @return Object[][] "matriz de objetos que contiene los datos del rs"
     */
    public Object[][] ResultSetToArray(ResultSet rs){
    	Object obj[][]=null;
    	try{
    		rs.last();
    		ResultSetMetaData rsmd = rs.getMetaData();
    		int numCols = rsmd.getColumnCount();
    		int numFils =rs.getRow();
    		obj=new Object[numFils][numCols];
			int j = 0;
			rs.beforeFirst();
			while (rs.next()){
				for (int i=0;i<numCols;i++){
					obj[j][i]=rs.getObject(i+1);
				}
				j++;
			}
    	}
    	catch(Exception e){
    		System.out.print("error al convertir el RS en matriz de objetos");
    	}
	return obj;
    }
    
    
    /**
     * Pasa un resultSet a una Matriz de String
     * @param rs  "resultSet a convertir"
     * @return Object[][] "matriz de objetos que contiene los datos del rs"
     */
    public String[][] ResultSetToStringArray(ResultSet rs){
    	String obj[][]=null;
    	try{
    		rs.last();
    		ResultSetMetaData rsmd = rs.getMetaData();
    		int numCols = rsmd.getColumnCount();
    		int numFils =rs.getRow();
    		obj=new String[numFils][numCols];
			int j = 0;
			rs.beforeFirst();
			while (rs.next()){
				for (int i=0;i<numCols;i++){
					obj[j][i]=rs.getObject(i+1).toString();
				}
				j++;
			}
    	}
    	catch(Exception e){
    		System.out.print("error al convertir el RS en matriz de objetos");
    	}
	return obj;
    }
    
}
